package com.kangjia.model.m0.service;

import java.io.IOException;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.jayway.jsonpath.JsonPath;
import com.kangjia.general.dao.query.OperType;
import com.kangjia.general.dao.query.Param;
import com.kangjia.general.dao.query.QueryParam;
import com.kangjia.model.m0.dao.ReportDao;
import com.kangjia.model.m0.entity.Age;
import com.kangjia.model.m0.entity.User;

import groovy.lang.GroovyClassLoader;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Service
@Slf4j
public class ParseService {

	private Map<Integer, JSONObject> kpiMap = new HashMap<>();

	private Map<String, String> suggestMap = new HashMap<>();

	private Map<Integer, Object> groovy = new HashMap<>();

	@Autowired
	private ReportDao reportDao;
	@Autowired
	private JedisPool jedisPool;

	private ScriptEngine engine = null;

	public ParseService() {
		super();
		engine = new ScriptEngineManager().getEngineByName("JavaScript");
	}

	@PostConstruct
	public void init() {
		Jedis jedis = jedisPool.getResource();
		try {
			Set<String> sets = jedis.hkeys("kangjia.kpi.data");
			for (String key : sets) {
				String json = jedis.hget("kangjia.kpi.data", key.toString());
				JSONObject jsonNode = new JSONObject(json);
				kpiMap.put(Integer.parseInt(key), jsonNode);
				Set<String> rs = jedis.smembers("relation");
				for (String r : rs) {
					getSuggest(r);
				}
			}
		} finally {
			jedis.close();
		}
	}

	private Integer getSex(String json) {
		net.minidev.json.JSONArray o = JsonPath.read(json, "$..secondItems[?(@.targetId==3152)].*");
		if (o != null && o.size() > 0) {
			return 0; // 女
		}
		return 1;
	}

	private double age(Integer age, JSONObject firstObject) {
		Integer targetId = firstObject.getInt("targetId");
		if (!targetId.equals(3163) && !targetId.equals(3195)) {
			double f = new Double(age) + (new Double(88) - new Double(firstObject.getInt("score"))) / 12 * 10;
			Integer a = (int) Math.round(f);
			if (a > 90) {
				a = 90;
			} else if (a < 18) {
				a = 18;
			}
			firstObject.put("age", a);
			if (targetId.equals(3087)) {
				return f * 0.3;
			} else {
				return f * 0.1;
			}
		}
		return 0;
	}

	public String handlerKpi(String json, Integer age, String name, String mobile, User user) {
		Integer sex = getSex(json);
		org.json.JSONObject jsonObject = new org.json.JSONObject(json);
		jsonObject.put("sex", sex);
		jsonObject.put("cname", name == null ? "" : name);
		jsonObject.put("mobile", mobile == null ? "" : mobile);
		jsonObject.put("birth", user.getBirth() == null ? "" : user.getBirth());
		jsonObject.put("imgUrl", user.getImgUrl() == null ? "" : user.getImgUrl());
		jsonObject.put("openid", user.getOpenid() == null ? "" : user.getOpenid());
		jsonObject.put("uid", user.getUid());
		Integer totalScore = 0;
		if (!jsonObject.isNull("totalScore")) {
			totalScore = jsonObject.getInt("totalScore");
		}
		jsonObject.put("ps", getPS(totalScore)); // 身体健康状况
		jsonObject.put("percentage", percentage(age, totalScore)); // 年龄段百分比
		JSONArray firstArray = jsonObject.getJSONArray("healthIndex");
		List<String> abnormals = new ArrayList<>();
		double tage = 0;
		for (int i = 0; i < firstArray.length(); i++) {
			JSONObject firstObject = (JSONObject) firstArray.get(i);
			handlerKpiInfo(firstObject); // 指标名称及指标解释
			tage += age(age, firstObject);
			JSONArray secondArray = firstObject.getJSONArray("secondItems");
			for (int j = 0; j < secondArray.length(); j++) {
				JSONObject secondObject = (JSONObject) secondArray.get(j);
				handlerKpiInfo(secondObject); // 指标名称及指标解释
				JSONArray thirdArray = secondObject.getJSONArray("thirdItems");
				for (int k = 0; k < thirdArray.length(); k++) {
					JSONObject thirdObject = (JSONObject) thirdArray.get(k);
					handlerKpiInfo(thirdObject); // 指标名称及指标解释
					Integer abnormalLevel = thirdObject.getInt("abnormalLevel"); // 第三级指标轻度等级
					handlerThirdInfo(thirdObject, abnormalLevel); // 根据第三级指标轻度等级设置指导建议, 康加的健康建议信息
					Integer targetId = thirdObject.getInt("targetId"); // 三级指标
					Integer inspectStandard = thirdObject.getInt("inspectStandard"); // 检查标准
					if (inspectStandard > 1) { // 检查标准非正常
						// 膳食建议处理,根据规则分析数据
						String key = targetId + "." + "1" + "." + inspectStandard;
						JSONArray array = handlerIndexInfo(key, thirdObject, abnormalLevel, sex);
						if (array != null) {
							thirdObject.put("diet", array);
						}
						// 营养建议处理,根据规则分析数据
						key = targetId + "." + "2" + "." + inspectStandard;
						array = handlerIndexInfo(key, thirdObject, abnormalLevel, sex);
						if (array != null) {
							thirdObject.put("nutrition", array);
						}
						// 运动和生活方式建议
						key = targetId + "." + "3" + "." + inspectStandard;
						array = handlerIndexInfo(key, thirdObject, abnormalLevel, sex);
						if (array != null) {
							thirdObject.put("motion", array);
						}
						abnormals.add(thirdObject.getString("targetName")); // 异常指标输出临时存储
					}
				}
			}
		}
		jsonObject.put("age", (int) Math.round(tage)); // 生理年龄
		JSONArray fourArray = jsonObject.getJSONArray("healthStatus");
		for (int i = 0; i < fourArray.length(); i++) {
			JSONObject fourObject = (JSONObject) fourArray.get(i);
			handlerKpiInfo(fourObject); // 指标名称及指标解释
		}
		jsonObject.put("abnormalsSize", abnormals.size()); // 异常指标数量
		if (abnormals.size() > 0) {
			jsonObject.put("abnormals", abnormals.toString().replaceAll("\\[\\]", "").replaceAll(",", "、")); // 异常指标
		}
		String jsonToString = jsonObject.toString();
		return jsonToString;
	}

	/**
	 * 建议指标处理
	 * 
	 * @param key
	 * @param jsonObject
	 * @param abnormalLevel
	 * @param sex
	 */
	private JSONArray handlerIndexInfo(String key, JSONObject jsonObject, Integer abnormalLevel, Integer sex) {
		JSONArray array = getSuggest(key); // 获取康加指标对应数据指标
		if (array == null || array.length() == 0) {
			return null;
		}
		for (int i = 0; i < array.length(); i++) {
			JSONObject indexTypeJsonObject = array.getJSONObject(i);
			indexTypeJsonObject.put("abnormalLevel", abnormalLevel); // 加入轻重程度,方便查询比较
			String personality = handlerPersonality(indexTypeJsonObject, abnormalLevel, sex); // 个性化建议结果
			if (StringUtils.isNotBlank(personality)) { // 个性化建议
				indexTypeJsonObject.put("personality", personality);
			}
			handlerProduct(indexTypeJsonObject, abnormalLevel); // 商品
		}
		return array;
	}

	/**
	 * 处理商品信息
	 * 
	 * @param indexTypeJsonObject
	 * @param abnormalLevel
	 */
	private void handlerProduct(JSONObject indexTypeJsonObject, Integer abnormalLevel) {
		if (!indexTypeJsonObject.isNull("product")) {
			JSONArray jsonArray = indexTypeJsonObject.getJSONArray("product");
			for (int j = 0; j < jsonArray.length(); j++) {
				JSONObject object = jsonArray.getJSONObject(j);
				object.put("abnormalLevel", abnormalLevel);
				switch (abnormalLevel) { // 服用意见
				case 2:
					object.put("info", object.getString("mild"));
					break;
				case 3:
				case 4:
					object.put("info", object.getString("moderate"));
					break;
				case 5:
					object.put("info", object.getString("severe"));
					break;
				}
				object.remove("mild");
				object.remove("moderate");
				object.remove("severe");
			}
		}
	}

	/**
	 * 获取json指定key
	 * 
	 * @param jsonObject
	 * @param key
	 * @return
	 */
	private String getString(JSONObject jsonObject, String key) {
		return !jsonObject.isNull(key) ? jsonObject.getString(key) : null;
	}

	/**
	 * 个性化建议处理
	 * 
	 * @param jsonObject
	 * @param abnormalLevel
	 * @param sex
	 * @return
	 */
	private String handlerPersonality(JSONObject jsonObject, Integer abnormalLevel, Integer sex) {
		if (!jsonObject.isNull("personality")) { // 个性化指标处理
			String format = jsonObject.getString("personality");
			String expression = jsonObject.getString("expression");
			if (StringUtils.isNoneBlank(expression)) {
				long s = System.currentTimeMillis();
				// format = script(sex, abnormalLevel, expression, format,
				// jsonObject.getInt("indexTypeId"));
				format = groovy(sex, abnormalLevel, expression, format, jsonObject.getInt("indexTypeId"));
				log.info("脚本耗时={}", System.currentTimeMillis() - s);
				return format;
			}
			return format;
		}
		return null;
	}

	/**
	 * 第三级指标,检测结果
	 * 
	 * @param jsonObject
	 */
	protected void handlerThirdInfo(JSONObject jsonObject, Integer abnormalLevel) {
		JSONObject kpi = getKpi(jsonObject.getInt("targetId"));
		if (abnormalLevel > 2) { // 中度异常以上检测结果,保存建议
			String suggestContent = getString(kpi, "suggestContent");
			if (StringUtils.isNotBlank(suggestContent)) {
				jsonObject.put("suggestContent", kpi.getString("suggestContent")); // 建议
			}
			String guideContent = getString(kpi, "guideContent");
			if (StringUtils.isNotBlank(guideContent)) {
				jsonObject.put("guideContent", kpi.getString("guideContent")); // 分析
			}
			String inprovementProgram = getString(kpi, "inprovementProgram");
			if (StringUtils.isNotBlank(inprovementProgram)) {
				jsonObject.put("inprovementProgram", kpi.getString("inprovementProgram")); // 改善方案
			}
		}
	}

	/**
	 * 设置指标名称,指标解释
	 * 
	 * @param jsonObject
	 */
	private void handlerKpiInfo(JSONObject jsonObject) {
		JSONObject kpi = getKpi(jsonObject.getInt("targetId"));
		jsonObject.put("targetName", kpi.get("inspectName"));
		jsonObject.put("inspectExplain", kpi.get("inspectExplain"));
	}

	/**
	 * 获取康加指标数据
	 * 
	 * @param key
	 * @return
	 */
	public JSONObject getKpi(Integer key) {
		JSONObject jsonNode = kpiMap.get(key);
		if (jsonNode == null) {
			Jedis jedis = jedisPool.getResource();
			try {
				String json = jedis.hget("kangjia.kpi.data", key.toString());
				jsonNode = new JSONObject(json);
			} finally {
				jedis.close();
			}
			kpiMap.put(key, jsonNode);
		}
		return jsonNode;
	}

	/**
	 * 获取康加指标关联建议指标数据,从内存加载,不存在从redis库加载
	 * 
	 * @param key
	 * @return
	 */
	public JSONArray getSuggest(String key) {
		String json = suggestMap.get(key);
		if (StringUtils.isBlank(json)) {
			JSONArray newJsonArray = new JSONArray();
			Jedis jedis = jedisPool.getResource();
			try {
				Set<String> sets = jedis.smembers(key); // 获取康加指标与建议指标设置关系
				if (sets == null) {
					return null;
				}
				boolean exe = false;
				for (String string : sets) {
					String j = jedis.hget("indexType", string); // 获取建议指标详细数据
					if (StringUtils.isNotBlank(j)) {
						JSONObject indexType = new JSONObject(j);
						String expression = indexType.getString("expression");
						if (StringUtils.isNoneBlank(expression)) {
							Object o = loadGroovy(indexType.getInt("indexTypeId"), expression); // 装载groovy脚本
							if (!exe) {
								try {
									o.getClass().getMethod("get", int.class, int.class, String.class, String.class,
											String.class).invoke(o, 1, 3, "执行", "X", "Y");
								} catch (Exception e) {
									log.info("初始执行失败", e);
								}
								exe = true;
							}
						}
						JSONArray jsonArray = new JSONArray();
						Map<String, String> maps = jedis.hgetAll("indexType_" + string); // 获取关联保健商品信息
						if (maps != null) {
							for (Entry<String, String> entry : maps.entrySet()) {
								String js = entry.getValue();
								if (StringUtils.isNotBlank(js)) {
									JSONObject product = new JSONObject(js);
									jsonArray.put(product);
								}
							}
							if (jsonArray.length() > 0) {
								indexType.put("product", jsonArray); // 建议指标关联保健商品信息
							}
						}
						newJsonArray.put(indexType);
					}
				}
			} finally {
				jedis.close();
			}
			if (newJsonArray.length() > 0) {
				suggestMap.put(key, newJsonArray.toString());
			}
		}
		return StringUtils.isNotBlank(json) ? new JSONArray(json) : null;
	}

	/**
	 * 动态脚本语言
	 * 
	 * @param sex
	 * @param lvl
	 * @param expression
	 * @param format
	 * @return
	 */
	public String script(Integer sex, Integer lvl, String expression, String format, Integer indexTypeId) {
		try {
			long s = System.currentTimeMillis();
			engine.put("sex", sex);
			engine.put("lvl", lvl);
			engine.eval(expression);
			String x = String.valueOf(engine.get("x"));
			String y = String.valueOf(engine.get("y"));
			log.info("执行时间={}", System.currentTimeMillis() - s);
			return format.replaceAll("X", x).replaceAll("Y", y);
		} catch (ScriptException e) {
			log.info("java script error", e);
		}
		return null;
	}

	/**
	 * 动态脚本语言
	 * 
	 * @param sex
	 * @param lvl
	 * @param expression
	 * @param format
	 * @return
	 */
	public String groovy(Integer sex, Integer lvl, String expression, String format, Integer indexTypeId) {
		Object obj = groovy.get(indexTypeId);
		if (obj == null) {
			obj = loadGroovy(indexTypeId, expression);
		}
		if (obj != null) {
			Object i;
			try {
				log.info("expression={}, sex={}, lvl={},format={},index={}", expression, sex, lvl, format, indexTypeId);
				long s = System.currentTimeMillis();
				i = obj.getClass().getMethod("get", int.class, int.class, String.class, String.class, String.class)
						.invoke(obj, sex, lvl, format, "X", "Y");
				log.info("执行时间={}", System.currentTimeMillis() - s);
				return String.valueOf(i);
			} catch (Exception e) {
				log.info("执行groovy异常", e);
			}
		}
		return null;
	}

	/**
	 * 装载groovy脚本
	 * 
	 * @param indexTypeId
	 * @param expression
	 * @return
	 */
	private Object loadGroovy(Integer indexTypeId, String expression) {
		Object obj = null;
		String createClass = "class Groovy" + indexTypeId + "{ "
				+ " public String get(int sex, int lvl, String format, String X, String Y) { def x, y; " + expression
				+ " " + "  \n return " + " format.replaceAll(X, String.valueOf(x)).replaceAll(Y, String.valueOf(y));"
				+ "}" + "}";
		GroovyClassLoader loader = new GroovyClassLoader();
		Class<?> newClazz = loader.parseClass(createClass);
		try {
			obj = newClazz.newInstance();
			groovy.put(indexTypeId, obj);
		} catch (Exception e) {
			log.info("创建groovy异常", e);
		} finally {
			try {
				loader.close();
			} catch (IOException e) {
				log.info("GroovyClassLoader关闭异常", e);
			}
		}
		return obj;
	}

	/**
	 * 身体状况
	 * 
	 * @param jsonObject
	 * @return
	 */
	public String getPS(Integer totalScore) {
		if (totalScore >= 95) {
			return "非常好";
		} else if (totalScore >= 90 && totalScore <= 94) {
			return "良好";
		} else if (totalScore >= 85 && totalScore <= 89) {
			return "轻度亚健康";
		} else if (totalScore < 85) {
			return "明显亚健康";
		}
		return null;
	}

	/**
	 * 清空内存操作
	 */
	public void clear() {
		suggestMap.clear(); // 清缓存数据
		kpiMap.clear();
		groovy.clear();
		init(); // 从新初始化
	}

	/**
	 * 年龄,分数统计7天数据
	 * 
	 * @param age
	 * @param totalScore
	 */
	public Integer percentage(Integer age, Integer totalScore) {
		AgeGroup group = new AgeGroup(age);
		int min = group.getMin();
		int max = group.getMax();
		LocalDate ld = LocalDate.now();
		Long startDate = Long.parseLong(ld.getYear() + "" + ld.getMonthValue() + "" + ld.getDayOfMonth());
		ld.minus(7, ChronoUnit.DAYS);
		Long endDate = Long.parseLong(ld.getYear() + "" + ld.getMonthValue() + "" + ld.getDayOfMonth());

		QueryParam param = new QueryParam();
		param.addParam(new Param<Integer>("age", OperType.ge, min));
		param.addParam(new Param<Integer>("age", OperType.le, max));
		param.addParam(new Param<Long>("lastTime", OperType.ge, startDate));
		param.addParam(new Param<Long>("lastTime", OperType.le, endDate));
		Integer total = reportDao.count(Age.class, param);
		if (total != null && total > 0) {
			param.addParam(new Param<Integer>("score", OperType.lt, totalScore));
			Integer t = reportDao.count(Age.class, param);
			if (t == null || t == 0) {
				return 10;
			}
			t = (int) ((float) t / (float) total * 100);
			if (t >= 100) {
				return 95;
			} else if (t <= 0) {
				return 10;
			}
			return t;
		}
		return 90;
	}

	private class AgeGroup {
		private int max;
		private int min;

		public AgeGroup(int age) {
			super();
			this.init(age);
		}

		public void init(int age) {
			if (age < 30) {
				min = 0;
				max = 29;
			} else if (age >= 30 && age < 40) {
				min = 30;
				max = 39;
			} else if (age >= 40 && age < 50) {
				min = 40;
				max = 49;
			} else if (age >= 50 && age < 60) {
				min = 50;
				max = 59;
			} else {
				min = 60;
				max = 300;
			}
		}

		public int getMax() {
			return max;
		}

		public int getMin() {
			return min;
		}
	}

}
